import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import subprocess
import threading
import time
import os
import queue
import re
import sys
import traceback
import logging
from logging.handlers import RotatingFileHandler
import random
import shutil

# ====================== 工具类 ======================
class ToolTip:
    """创建工具提示框，当鼠标悬停在控件上时显示提示信息"""
    def __init__(self, widget, text):
        self.widget = widget
        self.text = text
        self.tip_window = None
        self.widget.bind("<Enter>", self.show_tip)
        self.widget.bind("<Leave>", self.hide_tip)
    
    def show_tip(self, event=None):
        """显示工具提示"""
        if self.tip_window or not self.text:
            return
        x, y, _, _ = self.widget.bbox("insert")
        x = x + self.widget.winfo_rootx() + 25
        y = y + self.widget.winfo_rooty() + 25
        
        self.tip_window = tk.Toplevel(self.widget)
        self.tip_window.wm_overrideredirect(True)
        self.tip_window.wm_geometry(f"+{x}+{y}")
        
        label = tk.Label(self.tip_window, text=self.text, justify="left",
                         background="#ffffe0", relief="solid", borderwidth=1,
                         font=("tahoma", "8", "normal"))
        label.pack(ipadx=1)
    
    def hide_tip(self, event=None):
        """隐藏工具提示"""
        if self.tip_window:
            self.tip_window.destroy()
        self.tip_window = None

# ====================== 游戏管理类 ======================
class GameManager:
    """管理游戏数据库和设备状态"""
    GAME_DATABASE = {
        "纸片马力欧": {"dec_id": "2102104", "base": "00201358", "app": "00201358.app", "rec": "00201358.rec"},
        "动物森林": {"dec_id": "2104108", "base": "00201b2c", "app": "00201b2c.app", "rec": "00201b2c.rec"},
        "任天堂明星大乱斗": {"dec_id": "1201105", "base": "001253d1", "app": "001253d1.app", "rec": "001253d1.rec"},
        "塞尔达传说": {"dec_id": "2101104", "base": "00200f70", "app": "00200f70.app", "rec": "00200f70.rec"},
        "未来赛车": {"dec_id": "5202103", "base": "004f60b7", "app": "004f60b7.app", "rec": "004f60b7.rec"},
        "马力欧医生": {"dec_id": "6101104", "base": "005d1870", "app": "005d1870.app", "rec": "005d1870.rec"},
        "神游马力欧": {"dec_id": "1101104", "base": "0010cd30", "app": "0010cd30.app", "rec": "0010cd30.rec"},
        "马力欧卡丁车": {"dec_id": "5201105", "base": "004f5cd1", "app": "004f5cd1.app", "rec": "004f5cd1.rec"},
        "水上摩托": {"dec_id": "5101104", "base": "004dd630", "app": "004dd630.app", "rec": "004dd630.rec"},
        "星际火狐": {"dec_id": "4101105", "base": "003e93f1", "app": "003e93f1.app", "rec": "003e93f1.rec"},
        "越野摩托": {"dec_id": "5102108", "base": "004dda1c", "app": "004dda1c.app", "rec": "004dda1c.rec"},
        "罪与罚": {"dec_id": "4102103", "base": "003e97d7", "app": "003e97d7.app", "rec": "003e97d7.rec"},
        "组合机器人": {"dec_id": "2105103", "base": "00201f0f", "app": "00201f0f.app", "rec": "00201f0f.rec"},
        "耀西故事": {"dec_id": "1102101", "base": "0010d115", "app": "0010d115.app", "rec": "0010d115.rec"}
    }
    
    def __init__(self, logger=None):
        self.device_games = []
        self.logger = logger
    
    @property
    def injectable_games(self):
        """可注入的游戏列表"""
        return list(self.GAME_DATABASE.keys())
    
    def get_game_files(self, game_name):
        """返回游戏的所有可能文件"""
        game_info = self.GAME_DATABASE.get(game_name, {})
        return [game_info.get('app', ''), game_info.get('rec', '')]
    
    def get_preferred_file(self, game_name):
        """优先获取.app文件，如果没有则获取.rec文件"""
        game_info = self.GAME_DATABASE.get(game_name, {})
        return game_info.get('app') or game_info.get('rec', '')
    
    def update_game_status(self, game_name):
        """检查游戏是否存在于设备上"""
        game_files = self.get_game_files(game_name)
        for file in game_files:
            if file and file.lower() in self.device_games:
                return True
        return False
    
    def parse_game_list(self, log_text):
        """从aulon2输出中解析游戏列表"""
        self.device_games = []
        patterns = [
            r"([a-f0-9]{8}\.(?:app|rec)):\s*\d+",
            r"([a-f0-9]{8}\.(?:app|rec))",
            r"([a-f0-9]{8}\.(?:app|rec))\s+\d+",
            r"([a-f0-9]{8}\.(?:app|rec))\s+\d+\s*[KM]?i?B"
        ]
        
        matches = []
        for pattern in patterns:
            matches = re.findall(pattern, log_text, re.IGNORECASE)
            if matches:
                break
        
        # 备选解析方案
        if not matches:
            lines = log_text.split('\n')
            for line in lines:
                if ".app" in line.lower() or ".rec" in line.lower():
                    parts = line.split()
                    for part in parts:
                        if re.match(r"^[a-f0-9]{8}\.(app|rec)$", part, re.IGNORECASE):
                            matches.append(part)
        
        # 记录日志
        if self.logger:
            if not matches:
                self.logger.warning("在日志文本中未找到游戏文件")
            else:
                self.logger.info(f"在日志中找到 {len(matches)} 个游戏文件")
        
        self.device_games = [m.lower() for m in matches]
        return len(self.device_games)

# ====================== GUI主类 ======================
class Aulon2GUI:
    """神游卡管理程序的主界面类"""
    LANGUAGE_TEXTS = {
        "title": "神游卡管理程序（增补版） v0.63 by sksaUltra64",
        "connect": "连接设备",
        "refresh": "初始化",
        "remove": "删除选中",
        "operations": "设备操作",
        "gamelist": "游戏库",
        "console": "控制台",
        "details": "双击查看详情",
        "status_ready": "● 未连接",
        "status_connecting": "● 连接中...",
        "status_connected": "● 已连接",
        "status_failed": "● 连接失败",
        "status_lost": "● 连接丢失",
        "ready": "准备就绪 - 连接设备以查看游戏",
        "please_wait": "请稍候...",
        "confirm_delete": "确认删除",
        "delete_prompt": "删除此游戏文件？\n\n游戏: {}",
        "warning_connect": "请先连接设备",
        "warning_select": "请选择有效的游戏",
        "game_loading": "正在加载游戏列表...",
        "no_games": "未找到游戏。原始输出:",
        "game_count": "找到 {} 个游戏文件",
        "log_count": "解析了 {} 行",
        "game_details": "游戏详情",
        "game_name": "游戏: {}",
        "file_size": "大小: {}",
        "file_type": "类型: {}",
        "app_type": "游戏文件",
        "rec_type": "加密文件",
        "status_refreshing": "正在初始化...",
        "status_refreshed": "初始化完成",
        "status_deleting": "正在删除 {}...",
        "status_deleted": "{} 删除成功",
        "status_delete_failed": "{} 删除失败 - 查看日志",
        "injection": "游戏注入",
        "select_game": "选择游戏:",
        "inject_btn": "注入游戏",
        "inject_confirm": "确认注入",
        "inject_prompt": "将此游戏注入设备？\n\n游戏: {}\n\n",
        "injecting": "正在注入 {}...",
        "inject_success": "{} 注入成功",
        "inject_failed": "{} 注入失败",
        "inject_progress": "进度: {}%",
        "inject_progress_unknown": "正在注入...",
        "inject_file_not_found": "未找到游戏文件: {}\n请将文件放在程序目录中",
        "inject_waiting": "注入可能需要几分钟时间...",
        "progress_indeterminate": "处理中...",
        "alternate_exists": "存在替代文件类型",
        "inject_disabled": "注入已禁用",
        "retry_count": "重试中 ({}/{})",
        "storage_ready": "等待数据..."
    }
    
    def __init__(self, root):
        self.root = root
        self.root.title(self.LANGUAGE_TEXTS["title"])
        self.root.geometry("900x700")
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # 获取可执行文件所在目录（打包后）
        if getattr(sys, 'frozen', False):
            self.base_dir = os.path.dirname(sys.executable)
        else:
            self.base_dir = os.path.dirname(os.path.abspath(__file__))
        
        # 初始化状态变量
        self.process = None
        self.output_queue = queue.Queue()
        self.running = True
        self.refreshing = False
        self.current_operation = None
        self.temp_file_to_delete = None
        self.last_command = None
        self.last_command_success = {}
        self.storage_capacity = "未知"
        
        # 存储容量解析的临时变量
        self.free_value = None
        self.free_unit = None
        self.used_value = None
        self.used_unit = None
        
        # 单位转换系数 (KiB/MiB/GiB 到 MB)
        self.unit_conversion = {
            "KiB": 1/1024,   # KiB 转 MB
            "MiB": 1,         # MiB 转 MB
            "GiB": 1024       # GiB 转 MB
        }
        
        # 设置日志和游戏管理器
        self.setup_logging()
        self.game_manager = GameManager(logger=self.logger)
        
        # 检查aulon2.exe是否存在
        self.check_aulon2_exists()
        
        # 创建UI
        self.create_widgets()
        
        # 启动输出监控线程
        self.output_thread = threading.Thread(target=self.monitor_output, daemon=True)
        self.output_thread.start()
        
        self.log_message("程序已启动", "info")
        self.log_message(f"程序目录: {self.base_dir}", "info")
    
    # ====================== 日志和文件管理 ======================
    def setup_logging(self):
        """设置日志轮转"""
        try:
            log_dir = os.path.join(self.base_dir, "logs")
            os.makedirs(log_dir, exist_ok=True)
            
            log_file = os.path.join(log_dir, "ique_manager.log")
            handler = RotatingFileHandler(log_file, maxBytes=5*1024*1024, backupCount=3, encoding="utf-8")
            
            logging.basicConfig(
                level=logging.INFO,
                format="[%(asctime)s] %(levelname)s: %(message)s",
                handlers=[handler]
            )
            self.logger = logging.getLogger("ique_manager")
            
        except Exception as e:
            print(f"日志初始化失败: {str(e)}")
            self.logger = None
    
    def get_aulon2_dir(self):
        """返回 aulon2 文件夹的路径"""
        return os.path.join(self.base_dir, "aulon2")
    
    def log_message(self, message, level="info"):
        """将消息记录到控制台和文件"""
        if self.logger:
            log_func = getattr(self.logger, level, self.logger.info)
            log_func(message)
        
        try:
            self.console.config(state='normal')
            self.console.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {message}\n")
            self.console.see(tk.END)
            self.console.config(state='disabled')
        except Exception:
            pass
    
    def check_aulon2_exists(self):
        """检查 aulon2.exe 是否存在"""
        try:
            self.aulon2_dir = self.get_aulon2_dir()
            self.aulon2_path = os.path.join(self.aulon2_dir, 'aulon2.exe')
            
            if not os.path.exists(self.aulon2_path):
                messagebox.showerror(
                    "严重错误",
                    f"未找到 aulon2.exe!\n请确保它在目录中:\n{self.aulon2_dir}"
                )
                self.root.destroy()
                raise FileNotFoundError(f"在 {self.aulon2_dir} 中未找到 aulon2.exe")
            
        except Exception as e:
            messagebox.showerror("初始化错误", f"初始化失败: {str(e)}")
            self.root.destroy()
    
    # ====================== UI创建 ======================
    def create_widgets(self):
        """创建程序界面"""
        try:
            # 主框架
            main_frame = ttk.Frame(self.root)
            main_frame.pack(fill="both", expand=True, padx=10, pady=10)
            
            # 左侧面板 - 设备操作
            left_frame = ttk.LabelFrame(main_frame, text=self.LANGUAGE_TEXTS["operations"], width=250)
            left_frame.pack(side="left", fill="y", padx=5, pady=5)
            left_frame.pack_propagate(False)
            
            # 操作按钮
            self.create_operation_buttons(left_frame)
            
            # 右侧面板 - 游戏库
            game_library_frame = ttk.LabelFrame(main_frame, text=self.LANGUAGE_TEXTS["gamelist"])
            game_library_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)
            
            # 游戏列表
            self.create_game_list(game_library_frame)
            
            # 控制台
            console_frame = ttk.LabelFrame(self.root, text=self.LANGUAGE_TEXTS["console"])
            console_frame.pack(fill="both", expand=True, padx=10, pady=5)
            self.console = scrolledtext.ScrolledText(console_frame, wrap=tk.WORD, state='disabled', height=8, font=("Consolas", 9))
            self.console.pack(fill="both", expand=True, padx=5, pady=5)
            
            # 状态栏
            self.create_status_bar()
            
            # 进度条
            self.create_progress_bar()
            
        except Exception as e:
            self.log_message(f"UI初始化失败: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
            messagebox.showerror("UI错误", f"界面初始化失败: {str(e)}")
            self.root.destroy()
    
    def create_operation_buttons(self, parent):
        """创建操作按钮区域"""
        btn_frame = ttk.Frame(parent)
        btn_frame.pack(fill="x", padx=5, pady=10)
        
        self.connect_btn = ttk.Button(
            btn_frame, 
            text=self.LANGUAGE_TEXTS["connect"], 
            command=self.connect_device,
            width=15
        )
        self.connect_btn.pack(side="top", fill="x", padx=10, pady=10)
        
        self.refresh_btn = ttk.Button(
            btn_frame, 
            text=self.LANGUAGE_TEXTS["refresh"], 
            command=self.refresh_games,
            width=15
        )
        self.refresh_btn.pack(side="top", fill="x", padx=10, pady=10)
        
        # 状态指示器
        status_indicator_frame = ttk.Frame(parent)
        status_indicator_frame.pack(fill="x", padx=5, pady=10)
        
        ttk.Label(status_indicator_frame, text="状态:").pack(side="left", padx=5)
        self.connection_status = ttk.Label(
            status_indicator_frame, 
            text=self.LANGUAGE_TEXTS["status_ready"],
            foreground="red",
            font=("Arial", 9, "bold"),
            width=10
        )
        self.connection_status.pack(side="right", padx=5)
        
        # 存储容量显示
        storage_frame = ttk.Frame(parent)
        storage_frame.pack(fill="x", padx=5, pady=5)
        
        ttk.Label(storage_frame, text="神游卡", font=("Arial", 9)).pack(side="left", padx=(0, 5))
        self.storage_label = ttk.Label(
            storage_frame,
            text=self.LANGUAGE_TEXTS["storage_ready"],
            font=("Arial", 9)
        )
        self.storage_label.pack(side="left", padx=0)
    
    def create_game_list(self, parent):
        """创建游戏列表区域"""
        canvas = tk.Canvas(parent)
        scrollbar = ttk.Scrollbar(parent, orient="vertical", command=canvas.yview)
        self.scrollable_frame = ttk.Frame(canvas)
        
        self.scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        self.games_frame = self.scrollable_frame
        
        # 标题行
        title_frame = ttk.Frame(self.games_frame, style="Title.TFrame")
        title_frame.pack(fill="x", pady=(0, 5))
        
        ttk.Label(title_frame, text="游戏名称", font=("Arial", 10, "bold"), width=35).pack(side="left", padx=5)
        ttk.Label(title_frame, text="状态", font=("Arial", 10, "bold"), width=15).pack(side="left", padx=5)
        ttk.Label(title_frame, text="操作", font=("Arial", 10, "bold"), width=15).pack(side="left", padx=5)
        
        ttk.Separator(self.games_frame, orient="horizontal").pack(fill="x", pady=5)
        
        # 添加游戏列表
        self.add_game_list()
        
        # 标题行样式
        style = ttk.Style()
        style.configure("Title.TFrame", background="#f0f0f0")
    
    def create_status_bar(self):
        """创建状态栏"""
        status_bar = ttk.Frame(self.root)
        status_bar.pack(side="bottom", fill="x")
        
        self.status_var = tk.StringVar()
        self.status_var.set(self.LANGUAGE_TEXTS["ready"])
        
        status_label = ttk.Label(status_bar, textvariable=self.status_var, relief="sunken", anchor="w")
        status_label.pack(side="left", fill="x", expand=True)
    
    def create_progress_bar(self):
        """创建进度条"""
        self.progress_frame = ttk.Frame(self.root)
        self.progress_frame.pack_forget()
        
        self.progress = ttk.Progressbar(self.progress_frame, orient="horizontal", length=300, mode="determinate")
        self.progress.pack(side="left", padx=5, pady=5)
        
        self.progress_label = ttk.Label(self.progress_frame, text=self.LANGUAGE_TEXTS["ready"], font=("Arial", 9))
        self.progress_label.pack(side="left", padx=5)
    
    # ====================== 游戏列表管理 ======================
    def add_game_list(self):
        """向界面添加游戏列表"""
        for widget in self.games_frame.winfo_children():
            if widget not in [self.games_frame.winfo_children()[0], self.games_frame.winfo_children()[1]]:
                widget.destroy()
        
        for game_name in self.game_manager.injectable_games:
            game_frame = ttk.Frame(self.games_frame)
            game_frame.pack(fill="x", pady=3)
            
            name_label = ttk.Label(game_frame, text=game_name, width=35)
            name_label.pack(side="left", padx=5)
            
            status_var = tk.StringVar()
            status_var.set("检查中...")
            status_label = ttk.Label(game_frame, textvariable=status_var, width=15)
            status_label.pack(side="left", padx=5)
            
            btn_frame = ttk.Frame(game_frame)
            btn_frame.pack(side="left", padx=5)
            
            game_frame.game_info = {
                "game_name": game_name,
                "status_var": status_var,
                "btn_frame": btn_frame
            }
            
            self.update_game_status(game_frame)
    
    def update_game_status(self, game_frame):
        """根据设备状态更新游戏状态"""
        game_info = game_frame.game_info
        game_name = game_info["game_name"]
        btn_frame = game_info["btn_frame"]
        
        for widget in btn_frame.winfo_children():
            widget.destroy()
        
        game_exists = self.game_manager.update_game_status(game_name)
        
        if game_exists:
            game_info["status_var"].set("🎮")
            btn = ttk.Button(btn_frame, text="移除", width=8, command=lambda g=game_name: self.remove_game(g))
            btn.pack(side="left", padx=2)
        else:
            game_info["status_var"].set("🖥️")
            btn = ttk.Button(btn_frame, text="注入", width=8, command=lambda g=game_name: self.inject_selected_game(g))
            btn.pack(side="left", padx=2)
    
    # ====================== 设备操作 ======================
    def connect_device(self):
        """连接到iQue设备"""
        if self.process and self.process.poll() is None:
            self.log_message("已连接到设备", "info")
            return
        
        self.log_message("正在连接到iQue设备...", "info")
        self.connection_status.config(text=self.LANGUAGE_TEXTS["status_connecting"], foreground="blue")
        
        try:
            self.process = subprocess.Popen(
                [self.aulon2_path],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                encoding='utf-8',
                creationflags=subprocess.CREATE_NO_WINDOW,
                cwd=self.aulon2_dir
            )
            
            self.last_command = "B"
            self.last_command_success["B"] = False
            self.process.stdin.write("B\n")
            self.process.stdin.flush()
            
            threading.Timer(5.0, self.check_connect_timeout).start()
            
        except Exception as e:
            self.log_message(f"连接失败: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
            self.connection_status.config(text=self.LANGUAGE_TEXTS["status_failed"], foreground="red")
    
    def check_connect_timeout(self):
        """检查连接是否超时"""
        if not self.last_command_success.get("B", False):
            self.log_message("连接超时", "error")
            self.connection_status.config(text=self.LANGUAGE_TEXTS["status_failed"], foreground="red")
    
    def on_connect_success(self):
        """设备成功连接时的回调"""
        self.log_message("设备连接成功!", "info")
        self.connection_status.config(text=self.LANGUAGE_TEXTS["status_connected"], foreground="green")
        self.status_var.set(self.LANGUAGE_TEXTS["ready"])
        self.get_storage_info()
    
    def get_storage_info(self):
        """发送命令C获取存储信息"""
        if self.process and self.process.poll() is None:
            self.log_message("获取存储信息...", "info")
            # 重置存储解析状态
            self.free_value = None
            self.free_unit = None
            self.used_value = None
            self.used_unit = None
            self.run_command("C", delay=0.5, expect_success=False)
    
    def update_storage_capacity(self, free, used, unit):
        """更新存储容量显示"""
        try:
            # 确保值是数字
            free_value = float(free) * self.unit_conversion.get(unit, 1)
            used_value = float(used) * self.unit_conversion.get(unit, 1)
            
            # 格式化显示
            display_text = f"剩余: {free_value:.2f} MB | 已用: {used_value:.2f} MB"
            
            self.storage_capacity = display_text
            self.storage_label.config(text=self.storage_capacity)
            self.log_message(f"存储容量更新: {self.storage_capacity}", "info")
        except Exception as e:
            self.log_message(f"更新存储容量失败: {str(e)}", "error")
    
    def run_command(self, command, delay=2, expect_success=True, retries=3):
        """向aulon2进程发送命令，支持重试机制"""
        for attempt in range(retries):
            try:
                if self.process is None or self.process.poll() is not None:
                    self.log_message("进程未运行，尝试重新连接", "warning")
                    self.connect_device()
                    time.sleep(2)
                
                if self.process and self.process.poll() is None:
                    self.log_message(f">>> 执行命令: {command}", "info")
                    self.process.stdin.write(command + "\n")
                    self.process.stdin.flush()
                    
                    self.last_command = command
                    self.last_command_success[command] = False
                    
                    if expect_success:
                        start_time = time.time()
                        while time.time() - start_time < delay:
                            if self.last_command_success.get(command, False):
                                return True
                            time.sleep(0.1)
                    
                    return True
                
                return False
            except Exception as e:
                self.log_message(f"命令执行错误 (尝试 {attempt+1}/{retries}): {str(e)}", "error")
                self.log_message(traceback.format_exc(), "error")
                self.connection_status.config(text=self.LANGUAGE_TEXTS["status_lost"])
                
                if attempt < retries - 1:
                    time.sleep(1 + random.random())
        return False
    
    # ====================== 输出监控 ======================
    def monitor_output(self):
        """监控aulon2进程输出并检测命令响应"""
        try:
            while self.running:
                try:
                    if self.process and self.process.poll() is None:
                        output = self.process.stdout.readline()
                        if output:
                            stripped = output.strip()
                            if stripped:
                                self.log_message(stripped, "info")
                                self.output_queue.put(stripped)
                                
                                if "success" in stripped.lower() or "完成" in stripped:
                                    if self.last_command:
                                        self.last_command_success[self.last_command] = True
                                        self.last_command = None
                                
                                if "Init success" in stripped:
                                    self.last_command_success["B"] = True
                                    self.root.after(0, self.on_connect_success)
                                
                                # 存储容量解析 - 修复部分开始
                                if stripped.startswith("Free:") or stripped.startswith("Used:"):
                                    # 解析Free行
                                    if stripped.startswith("Free:"):
                                        match = re.search(r"Free:\s+(\d+)\s+\(([\d.]+)\s+(\w+)\)", stripped)
                                        if match:
                                            self.free_value = match.group(2)
                                            self.free_unit = match.group(3)
                                            self.log_message(f"解析到Free: {self.free_value} {self.free_unit}", "debug")
                                    
                                    # 解析Used行
                                    elif stripped.startswith("Used:"):
                                        match = re.search(r"Used:\s+(\d+)\s+\(([\d.]+)\s+(\w+)\)", stripped)
                                        if match:
                                            self.used_value = match.group(2)
                                            self.used_unit = match.group(3)
                                            self.log_message(f"解析到Used: {self.used_value} {self.used_unit}", "debug")
                                    
                                    # 当两者都解析到后更新UI
                                    if self.free_value is not None and self.used_value is not None:
                                        # 确保单位一致（优先使用Free的单位）
                                        unit = self.free_unit if self.free_unit else self.used_unit
                                        self.root.after(0, lambda: self.update_storage_capacity(
                                            self.free_value, self.used_value, unit
                                        ))
                                # 存储容量解析 - 修复部分结束
                                
                                # 检查注入进度
                                if self.current_operation == "inject":
                                    if "WriteFile success" in stripped:
                                        self.root.after(0, lambda: self.log_message("注入成功!", "info"))
                                        self.last_command_success[self.last_command] = True
                                    
                                    elif "error" in stripped.lower() or "fail" in stripped.lower():
                                        self.root.after(0, lambda: self.log_message("注入失败", "error"))
                                    
                                    progress_match = re.search(r"(\d+)%", stripped)
                                    if progress_match:
                                        progress = int(progress_match.group(1))
                                        self.root.after(0, lambda p=progress: self.update_progress(p))
                    else:
                        time.sleep(0.5)
                except Exception as e:
                    error_msg = f"输出监控错误: {str(e)}\n{traceback.format_exc()}"
                    self.log_message(error_msg, "error")
                    if self.connection_status:
                        self.root.after(0, lambda: self.connection_status.config(
                            text=self.LANGUAGE_TEXTS["status_lost"], foreground="orange"
                        ))
                    time.sleep(1)  # 避免快速错误循环
        except Exception as e:
            # 添加外层异常捕获
            error_msg = f"监控线程发生致命错误: {str(e)}\n{traceback.format_exc()}"
            self.log_message(error_msg, "critical")
            # 尝试通知UI线程
            if self.root:
                self.root.after(0, lambda: messagebox.showerror("线程崩溃", "监控线程已崩溃，程序可能不稳定。\n请查看日志获取详细信息。"))
    
    # ====================== 进度条管理 ======================
    def update_progress(self, value):
        """更新进度条显示"""
        try:
            self.progress.config(mode="determinate")
            self.progress['value'] = value
            self.progress_label.config(text=self.LANGUAGE_TEXTS["inject_progress"].format(value))
            self.status_var.set(f"正在注入游戏: {value}%")
        except Exception as e:
            self.log_message(f"进度条更新错误: {str(e)}", "error")
    
    def start_indeterminate_progress(self):
        """启动不确定进度条（流动效果）"""
        try:
            self.progress_frame.pack(fill="x", padx=5, pady=5)
            self.progress.config(mode="indeterminate")
            self.progress.start(10)
            self.progress_label.config(text=self.LANGUAGE_TEXTS["progress_indeterminate"])
            self.status_var.set(self.LANGUAGE_TEXTS["please_wait"])
        except Exception as e:
            self.log_message(f"启动进度流动效果失败: {str(e)}", "error")
    
    def stop_progress(self):
        """停止进度条并重置为就绪状态，隐藏进度区域"""
        try:
            if self.progress.cget("mode") == "indeterminate":
                self.progress.stop()
            
            self.progress.config(mode="determinate")
            self.progress['value'] = 0
            self.progress_label.config(text=self.LANGUAGE_TEXTS["ready"])
            self.status_var.set(self.LANGUAGE_TEXTS["ready"])
            self.progress_frame.pack_forget()
        except Exception as e:
            self.log_message(f"停止进度条失败: {str(e)}", "error")
    
    # ====================== 游戏操作 ======================
    def refresh_games(self):
        """使用命令5初始化设备上的游戏列表"""
        if not self.process or self.process.poll() is not None:
            messagebox.showwarning(self.LANGUAGE_TEXTS["warning_connect"], self.LANGUAGE_TEXTS["warning_connect"])
            return
        
        if self.refreshing:
            self.log_message("初始化进行中，请稍候...", "info")
            return
            
        self.refreshing = True
        self.current_operation = "refresh"
        self.log_message("正在使用命令5初始化设备游戏列表...", "info")
        self.status_var.set(self.LANGUAGE_TEXTS["status_refreshing"])
        self.refresh_btn.config(state='disabled')
        self.start_indeterminate_progress()
        threading.Thread(target=self._refresh_games_thread, daemon=True).start()
    
    def _refresh_games_thread(self):
        """使用命令5刷新游戏列表的后台线程"""
        try:
            while not self.output_queue.empty():
                try:
                    self.output_queue.get_nowait()
                except queue.Empty:
                    break
            
            if self.run_command("5", delay=0.5, expect_success=False):
                self.log_message("游戏列表命令发送成功", "info")
                
                output_lines = []
                start_time = time.time()
                timeout = 5.0
                
                time.sleep(1.0)
                
                while time.time() - start_time < timeout:
                    try:
                        while not self.output_queue.empty():
                            line = self.output_queue.get_nowait()
                            output_lines.append(line)
                            
                            if ">>>" in line or "aulon>" in line or "Game list refresh complete" in line:
                                break
                    except queue.Empty:
                        pass
                    
                    if output_lines and any(".app" in line or ".rec" in line for line in output_lines):
                        break
                        
                    time.sleep(0.2)
                
                if not output_lines:
                    self.log_message("未收到输出，尝试获取剩余行", "warning")
                    try:
                        while not self.output_queue.empty():
                            line = self.output_queue.get_nowait()
                            output_lines.append(line)
                    except:
                        pass
                
                raw_output = "\n".join(output_lines)
                self.log_message(f"'5'命令的原始输出:\n{raw_output}", "debug")
                
                game_count = self.game_manager.parse_game_list(raw_output)
                
                if game_count > 0:
                    self.log_message(f"找到 {game_count} 个游戏", "info")
                    self.status_var.set(f"找到 {game_count} 个游戏")
                else:
                    self.log_message("在设备输出中未找到游戏", "warning")
                    self.status_var.set("未找到游戏")
                
                self.root.after(0, self._update_game_ui)
                self.get_storage_info()
            else:
                self.log_message("发送游戏列表命令失败", "error")
                self.status_var.set("初始化失败")
                
        except Exception as e:
            self.log_message(f"初始化线程错误: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
            self.status_var.set("初始化错误")
        finally:
            self.refreshing = False
            self.current_operation = None
            self.root.after(0, self._finish_refresh)
    
    def _update_game_ui(self):
        """更新UI中的游戏状态"""
        try:
            for child in self.games_frame.winfo_children():
                if hasattr(child, "game_info"):
                    self.root.after(0, lambda c=child: self.update_game_status(c))
        except Exception as e:
            self.log_message(f"游戏列表更新错误: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
    
    def inject_selected_game(self, game_name):
        """注入选定的游戏"""
        if not self.process or self.process.poll() is not None:
            messagebox.showwarning(self.LANGUAGE_TEXTS["warning_connect"], self.LANGUAGE_TEXTS["warning_connect"])
            return
        
        game_info = self.game_manager.GAME_DATABASE.get(game_name)
        if not game_info:
            error_msg = f"未找到游戏信息: {game_name}"
            self.log_message(error_msg, "error")
            messagebox.showerror("游戏未找到", error_msg)
            return
            
        dec_id = str(game_info["dec_id"])
        hex_app = game_info["app"]
        original_path = os.path.join(self.aulon2_dir, dec_id)
        temp_path = os.path.join(self.aulon2_dir, hex_app)
        
        if os.path.exists(original_path):
            try:
                shutil.copy(original_path, temp_path)
                filename = hex_app
                self.temp_file_to_delete = temp_path
                self.log_message(f"已创建临时副本: {dec_id} -> {hex_app}", "info")
            except Exception as e:
                self.log_message(f"创建临时文件失败: {str(e)}", "error")
                messagebox.showerror("文件错误", f"无法创建临时文件: {str(e)}")
                return
        elif os.path.exists(os.path.join(self.aulon2_dir, game_info["app"])):
            filename = game_info["app"]
            self.temp_file_to_delete = None
        elif os.path.exists(os.path.join(self.aulon2_dir, game_info["rec"])):
            filename = game_info["rec"]
            self.temp_file_to_delete = None
        else:
            error_msg = self.LANGUAGE_TEXTS["inject_file_not_found"].format(f"{dec_id} 或 {game_info['app']} 或 {game_info['rec']}")
            self.log_message(error_msg, "error")
            messagebox.showerror("文件未找到", error_msg)
            return
            
        confirm = messagebox.askyesno(
            self.LANGUAGE_TEXTS["inject_confirm"], 
            self.LANGUAGE_TEXTS["inject_prompt"].format(game_name) + 
            "\n" + self.LANGUAGE_TEXTS["inject_waiting"]
        )
        
        if not confirm:
            if hasattr(self, 'temp_file_to_delete') and self.temp_file_to_delete and os.path.exists(self.temp_file_to_delete):
                try:
                    os.remove(self.temp_file_to_delete)
                except:
                    pass
            return
        
        self.log_message(f"开始游戏注入: {game_name} ({filename})", "info")
        self.current_operation = "inject"
        self.status_var.set(self.LANGUAGE_TEXTS["injecting"].format(game_name))
        self.connect_btn.config(state="disabled")
        self.refresh_btn.config(state="disabled")
        self.start_indeterminate_progress()
        self.progress['value'] = 0
        self.progress_label.config(text=self.LANGUAGE_TEXTS["inject_progress_unknown"])
        
        command = f"4 {filename}"
        self.log_message(f"执行注入命令: {command}", "info")
        
        threading.Thread(target=self._inject_game_thread, args=(command, game_name, filename), daemon=True).start()
    
    def _inject_game_thread(self, command, game_name, filename):
        """游戏注入的后台线程"""
        completed = False
        try:
            if self.run_command(command, delay=2, retries=3):
                self.log_message(f"{game_name} 注入开始...", "info")
                self.status_var.set(f"{game_name} 注入中...")
                
                start_time = time.time()
                last_output = ""
                timeout = 150
                
                while time.time() - start_time < timeout:
                    if self.process.poll() is not None:
                        break
                    
                    output_lines = []
                    while not self.output_queue.empty():
                        try:
                            line = self.output_queue.get_nowait()
                            output_lines.append(line)
                            last_output += line + "\n"
                            
                            if "WriteFile success" in line:
                                self.log_message(f"{game_name} 注入成功!", "info")
                                completed = True
                                break
                            
                            if "error" in line.lower() or "fail" in line.lower():
                                self.log_message(f"{game_name} 注入失败", "error")
                                break
                            
                            progress_match = re.search(r"(\d+)%", line)
                            if progress_match:
                                progress = int(progress_match.group(1))
                                self.root.after(0, lambda p=progress: self.update_progress(p))
                            
                        except queue.Empty:
                            pass
                    
                    if completed:
                        break
                    
                    time.sleep(1)
                
                if not completed:
                    self.log_message(f"{game_name} 注入超时", "error")
                    self.root.after(0, lambda: self.log_message(f"最后输出:\n{last_output}", "error"))
            else:
                self.log_message(f"{game_name} 注入失败 - 命令未发送", "error")
                
        except Exception as e:
            self.log_message(f"注入线程错误: {str(e)}\n{traceback.format_exc()}", "error")
        finally:
            if hasattr(self, 'temp_file_to_delete') and self.temp_file_to_delete and os.path.exists(self.temp_file_to_delete):
                try:
                    os.remove(self.temp_file_to_delete)
                    self.log_message(f"已删除临时文件: {self.temp_file_to_delete}", "info")
                except Exception as e:
                    self.log_message(f"删除临时文件失败: {str(e)}", "error")
            
            if completed:
                time.sleep(3)
                self.root.after(0, self.refresh_games)
            else:
                self.root.after(0, self.stop_progress)
            
            self.root.after(0, self._enable_buttons)
            self.current_operation = None
            self.status_var.set(f"{game_name} 注入完成" if completed else f"{game_name} 注入失败")
    
    def remove_game(self, game_name):
        """移除选定的游戏"""
        if not self.process or self.process.poll() is not None:
            messagebox.showwarning(self.LANGUAGE_TEXTS["warning_connect"], self.LANGUAGE_TEXTS["warning_connect"])
            return
        
        filenames = self.game_manager.get_game_files(game_name)
        if not filenames:
            error_msg = f"未找到游戏文件: {game_name}"
            self.log_message(error_msg, "error")
            messagebox.showerror("文件未找到", error_msg)
            return
        
        confirm = messagebox.askyesno(
            self.LANGUAGE_TEXTS["confirm_delete"], 
            self.LANGUAGE_TEXTS["delete_prompt"].format(game_name)
        )
        
        if not confirm:
            return
        
        self.log_message(f"开始游戏移除: {game_name}", "info")
        self.status_var.set(self.LANGUAGE_TEXTS["status_deleting"].format(game_name))
        self.refresh_btn.config(state="disabled")
        self.start_indeterminate_progress()
        threading.Thread(target=self._remove_game_thread, args=(game_name, filenames), daemon=True).start()
    
    def _remove_game_thread(self, game_name, filenames):
        """游戏移除的后台线程 - 移除所有相关文件"""
        try:
            success = True
            for file in filenames:
                if not file:
                    continue
                    
                if self.run_command(f"6 {file}", delay=1, retries=3):
                    self.log_message(f"{file} 移除成功!", "info")
                else:
                    self.log_message(f"{file} 移除失败", "error")
                    success = False
            
            if success:
                self.log_message(f"{game_name} 的所有文件已成功移除!", "info")
                self.status_var.set(self.LANGUAGE_TEXTS["status_deleted"].format(game_name))
            else:
                self.log_message(f"{game_name} 的部分文件移除失败", "warning")
                self.status_var.set(self.LANGUAGE_TEXTS["status_delete_failed"].format(game_name))
                
            time.sleep(1.5)
            self.root.after(0, self.refresh_games)
        except Exception as e:
            self.log_message(f"移除线程错误: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
            self.root.after(0, self._finish_refresh)
    
    # ====================== 辅助方法 ======================
    def _finish_refresh(self):
        """刷新后恢复UI状态"""
        try:
            if not self.refreshing:
                self.refresh_btn.config(state='normal')
            self.stop_progress()
        except Exception as e:
            self.log_message(f"刷新完成错误: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
    
    def _enable_buttons(self):
        """启用所有按钮"""
        try:
            self.connect_btn.config(state="normal")
            self.refresh_btn.config(state="normal")
            self.current_operation = None
        except Exception as e:
            self.log_message(f"按钮状态恢复错误: {str(e)}", "error")
            self.log_message(traceback.format_exc(), "error")
    
    def on_closing(self):
        """关闭窗口时的清理操作"""
        try:
            self.log_message("程序正在关闭...", "info")
            self.running = False  # 通知线程退出
            
            # 等待监控线程退出
            if self.output_thread.is_alive():
                self.log_message("等待监控线程退出...", "info")
                self.output_thread.join(2.0)  # 最多等待2秒
                
            # 关闭进程
            if self.process and self.process.poll() is None:
                try:
                    self.log_message("终止aulon2进程...", "info")
                    self.process.terminate()
                    self.log_message("已从设备断开连接", "info")
                    time.sleep(0.5)  # 给进程退出时间
                except Exception as e:
                    self.log_message(f"终止进程失败: {str(e)}", "error")
            
            # 清理临时文件
            if hasattr(self, 'temp_file_to_delete') and self.temp_file_to_delete and os.path.exists(self.temp_file_to_delete):
                try:
                    os.remove(self.temp_file_to_delete)
                    self.log_message(f"已清理临时文件: {self.temp_file_to_delete}", "info")
                except Exception as e:
                    self.log_message(f"清理临时文件失败: {str(e)}", "error")
            
            # 关闭日志处理程序
            if self.logger:
                for handler in self.logger.handlers:
                    handler.close()
                    self.log_message(f"已关闭日志处理器: {handler}", "info")
            
        except Exception as e:
            error_msg = f"关闭错误: {str(e)}\n{traceback.format_exc()}"
            self.log_message(error_msg, "error")
            print(error_msg)
        finally:
            self.root.destroy()

# ====================== 异常处理 ======================
def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
    """处理未捕获的异常"""
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    
    script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
    error_log = os.path.join(script_dir, "crash.log")
    
    with open(error_log, "a", encoding="utf-8") as f:
        f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 程序崩溃\n")
        f.write(f"错误类型: {exc_type.__name__}\n")
        f.write(f"错误消息: {str(exc_value)}\n")
        f.write(f"堆栈跟踪:\n{''.join(traceback.format_tb(exc_traceback))}\n")
        f.write("-" * 80 + "\n")
    
    try:
        root = tk.Tk()
        root.withdraw()
        messagebox.showerror(
            "程序崩溃", 
            f"发生未处理的异常:\n{str(exc_value)}\n\n" +
            f"详细信息已保存到:\n{error_log}\n\n" +
            "请将此文件提供给开发人员以进行诊断。"
        )
        root.destroy()
    except:
        print(f"程序崩溃: {str(exc_value)}")
        print(f"详细信息已保存到: {error_log}")

# ====================== 主程序入口 ======================
if __name__ == "__main__":
    sys.excepthook = handle_uncaught_exception
    
    try:
        root = tk.Tk()
        app = Aulon2GUI(root)
        root.mainloop()
    except Exception as e:
        handle_uncaught_exception(type(e), e, e.__traceback__)